﻿using Newtonsoft.Json.Linq;
using SimpleX.Sys;
using SimpleX.Tracing;

namespace SimpleX.RBAC
{
    public class AuthService : IAuthService
    {
        private readonly ISysUserService _sysUserService;
        private readonly ICacheService _cacheService;
        private readonly ICapPublisher _capPublisher;
        private readonly IConfigService _configService;
        private readonly ITracingService _tracingService;

        public AuthService(ISysUserService sysUserService,
            ICacheService cacheService,
            ICapPublisher capPublisher,
            IConfigService configService,
            ITracingService tracingService)
        {
            _sysUserService = sysUserService;
            _cacheService = cacheService;
            _capPublisher = capPublisher;
            _configService = configService;
            _tracingService = tracingService;
        }

        public async Task<LoginUserOutput> GetLoginUser()
        {
            var user = await _sysUserService.GetSysUserById(UserManager.UserId);//根据账号获取用户信息
            if (user != null)
            {
                var output = user.Adapt<LoginUserOutput>();
                output.ButtonCodeList = await _sysUserService.OwnButtonCodeList();
                return output;
            }
            throw Ex.Business("用户不存在");
        }

        public bool CheckTokenInRedis(string token)
        {
            if (string.IsNullOrWhiteSpace(UserManager.UserId) || string.IsNullOrWhiteSpace(UserManager.Device))
            {
                return false;
            }
            var key = CacheConst.Cache_UserToken + UserManager.UserId + ":" + UserManager.Device;
            var redisToken = _cacheService.Get<string>(key);
            return token == redisToken;
        }

        public async Task<LoginOutput> Login(LoginInput loginInput)
        {
            _tracingService.LogEvent("执行登录");

            loginInput.Password = PwdUtils.Decrypt(loginInput.Password);//解密
            if (string.IsNullOrWhiteSpace(loginInput.Password))
                return Unify.SetError("RSA配置错误,请联系管理员配置");


            _tracingService.LogEvent("开始查询");

            var user = await _sysUserService.GetSysUserByAccount(loginInput.Account);

            _tracingService.LogEvent("结束查询");

            user = await ExecuteAutoRegister(user, loginInput);

            if (user == null)
                return Unify.SetError("用户名密码错误");

            if (user.UserStatus != "Y")
                return Unify.SetError("用户名已经被禁用");

            user.Password = PwdUtils.Decrypt(user.Password); //解密

            LoginOutput loginOutput = null;
            switch (user.AuthenticationType)
            {
                case AuthenticationType.DataBase:
                    loginOutput = CheckDataBase(user, loginInput);
                    break;

                case AuthenticationType.LDAP:
                    loginOutput = await CheckLDAP(user, loginInput);
                    break;

                default:
                    break;
            }

            if (loginOutput.Token != "OK")
            {
                if (loginOutput.MustChangePwd)//需要修改密码
                    return loginOutput;
                else
                    return Unify.SetError(loginOutput.Token);
            }

            var token = JwtHelper.GenerateToken(new Dictionary<string, string>
            {
                [ClaimConst.UserId] = user.Id,
                [ClaimConst.Account] = user.Account,
                [ClaimConst.Name] = user.Name,
                [ClaimConst.SuperAdmin] = user.SuperAdmin,
                [ClaimConst.Device] = loginInput.Device
            });

            var key = CacheConst.Cache_UserToken + user.Id + ":" + loginInput.Device;
            _cacheService.Set(key, token, JwtHelper._jwtSettings.ExpirationMinutes * 60);

            _capPublisher.Publish("logined", user.Id);

            App.HttpContext.Response.Headers["access-token"] = token;
            //App.HttpContext.Response.Headers["x-access-token"] = token;

            return new LoginOutput
            {
                Token = token,
                Account = user.Account,
                Name = user.Name,
            };
        }

        private async Task<SysUser> ExecuteAutoRegister(SysUser user, LoginInput input)
        {
            if (user == null)
            {
                try
                {
                    var devConfigs = await _configService.GetListByCategory("AUTHENTICATION_REGISTER");
                    if (devConfigs != null)
                    {
                        var enable = devConfigs.FirstOrDefault(x => x.ConfigKey == "ENABLE").ConfigValue;
                        if (enable == "true")
                        {
                            var from = (AuthenticationType)devConfigs.First(x => x.ConfigKey == "DATA_FROM").ConfigValue.ToInt();
                            var orgId = devConfigs.First(x => x.ConfigKey == "DEFAULT_DEP").ConfigValue;
                            var mapJson = devConfigs.First(x => x.ConfigKey == "DATA_MAP").ConfigValue;

                            var userAddInput = new UserAddInput
                            {
                                Account = input.Account,
                                OrgId = orgId,
                                SuperAdmin = "N",
                                CreateDate = DateTime.Now,
                                CreateUserId = from.ToString(),
                                CreateUserName = $"自动注册:{from}",
                                Id = IDUtils.GetId(),
                                AuthenticationType = from
                            };

                            if (from == AuthenticationType.LDAP)
                            {
                                var helper = await GetLdapHelper();
                                var checkUser = await helper.CheckUser(input.Account, input.Password);
                                if (!checkUser.ValidPassword)
                                {
                                    return null;
                                }
                                SetUserAttribute(userAddInput, mapJson, checkUser);
                            }
                            else if (from == AuthenticationType.DataBase)
                            {
                                userAddInput.Name = userAddInput.Account;
                                userAddInput.LastLoginTime = DateTime.Now;
                                userAddInput.Password = PwdUtils.Encrypt(input.Password);
                            }
                            var roleList = devConfigs.FirstOrDefault(x => x.ConfigKey == "DEFAULT_ROLES").ConfigValue.Split(',').ToList();
                            await _sysUserService.Add(userAddInput);
                            await _sysUserService.GrantRole(new UserGrantRoleInput
                            {
                                Id = userAddInput.Id,
                                RoleIdList = roleList
                            });
                        }
                    }
                    user = await _sysUserService.GetSysUserByAccount(input.Account);
                }
                catch (Exception)
                {
                    throw new Exception("用户不存在");
                }
            }

            return user;
        }

        private void SetUserAttribute(UserAddInput userAddInput, string mapJson, LadpUserInfo checkUser)
        {
            // 解析JSON字符串
            JObject jsonObject = JObject.Parse(mapJson);

            // 获取键和值
            foreach (var property in jsonObject.Properties())
            {
                string key = property.Name;
                string value = property.Value.ToString();

                switch (key)
                {
                    case "email":
                        userAddInput.Email = checkUser.Attributes.GetValueOrDefault(value);
                        break;

                    case "name":
                        userAddInput.Name = checkUser.Attributes.GetValueOrDefault(value);
                        break;

                    default:
                        break;
                }
            }
        }

        private async Task<LoginOutput> CheckLDAP(SysUser user, LoginInput loginInput)
        {
            var helper = await GetLdapHelper();
            var checkUser = await helper.CheckUser(loginInput.Account, loginInput.Password);
            if (!checkUser.ValidPassword)
            {
                return new LoginOutput { Token = "用户名密码错误,请使用LDAP(OA)账号登录" };
            }
            return new LoginOutput { Token = "OK" };
        }

        private async Task<LdapHelper> GetLdapHelper()
        {
            var devConfigs = await _configService.GetListByCategory("AUTHENTICATION_LDAP");

            return new LdapHelper(new LdapSetting
            {
                BindingDN = devConfigs.First(x => x.ConfigKey == "BINDINGDN").ConfigValue,
                BindingPassword = devConfigs.First(x => x.ConfigKey == "BINDINGPASSWORD").ConfigValue,
                Port = devConfigs.First(x => x.ConfigKey == "PORT").ConfigValue.ToInt(),
                Server = devConfigs.First(x => x.ConfigKey == "SERVER").ConfigValue,
                UserOU = devConfigs.First(x => x.ConfigKey == "USEROU").ConfigValue,
                UserFifter = devConfigs.First(x => x.ConfigKey == "FIFTER").ConfigValue
            });
        }

        private LoginOutput CheckDataBase(SysUser user, LoginInput loginInput)
        {
            if (user.Password == loginInput.Password)
            {
                //返回结果 上次登录为空,或者距离上次登录已经30天了,则需要修改密码
                if (user.LastLoginTime == null || Math.Abs((DateTime.Now - user.LastLoginTime.Value).TotalDays) >= 30)
                {
                    return new LoginOutput
                    {
                        Token = "PLEASE CHANGE PASSWORD FIRST",
                        Account = user.Account,
                        Name = user.Name,
                        MustChangePwd = true
                    };
                }
                return new LoginOutput { Token = "OK" };
            }
            else
            {
                return new LoginOutput { Token = "用户名密码错误" };
            }
        }

        public Task LoginOut(LoginOutInput input)
        {
            var key = CacheConst.Cache_UserToken + UserManager.UserId + ":" + input.Device;
            _cacheService.Remove(key);

            return Task.CompletedTask;
        }

        public PicValidCodeOutPut GetCaptchaInfo()
        {
            //生成验证码
            var captchInfo = CaptchaUtil.CreateCaptcha(CaptchaType.ARITH, 4, 100, 38);
            //生成请求号，并将验证码放入redis
            var reqNo = IDUtils.GetId();
            _cacheService.Set(CacheConst.Cache_Captcha + reqNo, captchInfo.Code, TimeSpan.FromMinutes(5));
            //返回验证码和请求号
            return new PicValidCodeOutPut { ValidCodeBase64 = captchInfo.Base64Str, ValidCodeReqNo = reqNo };
        }

        [CapSubscribe("logined")]
        public async Task LoginedAsync(string userId)
        {
            await _sysUserService.SetLogined(userId);
        }
    }
}